/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
/*
 * bocciawii
 * Copyright (C) Grupo de trabajo ULE 2010 <ulewii@googlegroups.com>
 * 
 * teclado is free software: you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * teclado is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License along
 * with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "linked-container.h"

LinkedContainer::LinkedContainer(int x, int y)
{
	_bgColor[0] = .2;
	_bgColor[1] = .2;
	_bgColor[2] = .7;
	
	_bgImage = -1;
	
	_transparency = .5;


	_posActual = 0;
	
	_style = STYLE_NO_BORDER | STYLE_SOFT | STYLE_BG;
	_visible = 1;

	_posImg.x_inicio = 0;
	_posImg.y_inicio = 0;
	_posImg.x_final = 0;
	_posImg.y_final = 0;
	_posImg.alignment = 0;
	_posImg.ancho = 0;
	_posImg.alto = 0;
	
	_posGlobal.x_inicio = 0;
	_posGlobal.x_final = 0;
	_posGlobal.y_inicio = 0;
	_posGlobal.y_final = 0;
	_posGlobal.ancho = 0;
	_posGlobal.alto = 0;

	_pantallaX = x;
	_pantallaY = y;

	_spacing = 20;
	_highlighted = 0;

}

LinkedContainer::~LinkedContainer()
{
	for (int i = 0; i < _posActual; i++)
		elems[i]->~Elemento();
	glDeleteTextures(1, &_bgImage);
}

void LinkedContainer::SetBGColor(float* color)
{
	for (int i = 0; i < 3; i ++)
		_bgColor[i] = color[i];
}

void LinkedContainer::SetBGTransparency(float trans)
{
	_transparency = trans;
}

void LinkedContainer::SetBGImage(std::string nombre, int tam_x, int tam_y, int img_align)
{
	// No hay necesidad de darle la vuelta a la imagen
	//  ya ue ahora estamos en 2D, y nuestro sistema de coordenadas
	//  es idéntido al de SDL, así que guay :D
	SDL_Surface *imagen;
	GLenum formato_textura;

	imagen = LoadPNG(RUTA_IMAGENES + nombre);
    if (!imagen) 
	{
		//TODO gestión errore;
	}

	
	if (imagen->format->BytesPerPixel == 4)     // contains an alpha channel
	{
		if (imagen->format->Rmask == 0x000000ff)
			formato_textura = GL_RGBA;
		else
			formato_textura = GL_BGRA;
	} else if (imagen->format->BytesPerPixel == 3)     // no alpha channel
	{
		if (imagen->format->Rmask == 0x000000ff)
			formato_textura = GL_RGB;
		else
			formato_textura = GL_BGR;
	} else {
		printf("warning: the image is not truecolor..  this will probably break\n");
		// TODO Gestión errores
	}
	
    // Create Textures	   
	glGenTextures(1, &_bgImage);
	glBindTexture(GL_TEXTURE_2D, _bgImage);

	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
	glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);

	gluBuild2DMipmaps(GL_TEXTURE_2D, 4, imagen->w, imagen->h,formato_textura, GL_UNSIGNED_BYTE, imagen->pixels);


	_posImg.ancho = tam_x;
	_posImg.alto = tam_y;
	
	if (tam_x == -1)
		_posImg.ancho = imagen->w;
	if (tam_x == -1)
		_posImg.alto = imagen->h;

	_posImg.alignment = img_align;
	

	SDL_FreeSurface(imagen);
}

void LinkedContainer::SetStyle(int flags)
{
	_style = flags;
}

void LinkedContainer::SetVisible (int visible)
{
	_visible = visible;
}

void LinkedContainer::SetHighlight(int highlight)
{
	_highlighted = highlight;
}

int LinkedContainer::EsVisible()
{
	return _visible;
}

int LinkedContainer::EsHighlight()
{
	return _highlighted;
}

void LinkedContainer::Append(Elemento* elem)
{
	elems[_posActual] = elem;
	_posActual++;
}

Elemento* LinkedContainer::GetElemByID (std::string id)
{
	if (_posActual > 0)
		for (int i = 0; i < _posActual; i++)
		{
			if (elems[i]->GetID() == id)
				return elems[i];
		}
	return NULL;
}

int* LinkedContainer::GetCenter()
{
	int* centro = new int(2);
	centro[0] = _posGlobal.centro_x;
	centro[1] = _posGlobal.centro_y;
	return centro;
}

void LinkedContainer::Pack(int centro_x, int centro_y)
{
	// Chapucero, sólo para conseguir que un container sin contenidos
	// dibuje su imagen.
	if (_posActual == 0)
	{
		if (_bgImage == 0)
			return;

		_posGlobal.centro_x = centro_x;
		_posGlobal.centro_y = centro_y;
		_posGlobal.x_inicio = centro_x - _posImg.ancho / 2;
		_posGlobal.x_final = centro_x + _posImg.ancho / 2;
		_posGlobal.y_inicio = centro_y - _posImg.alto / 2;
		_posGlobal.y_final = centro_y + _posImg.alto / 2;
		_posGlobal.ancho = _posImg.ancho;
		_posGlobal.alto = _posImg.alto;
		
				_posImg.x_final = _posGlobal.x_final;
				_posImg.y_inicio = _posGlobal.y_inicio;
				_posImg.y_final = _posGlobal.y_final;
				_posImg.x_inicio = _posGlobal.x_inicio;
			return;
	}

	_posGlobal.centro_x = centro_x;
	_posGlobal.centro_y = centro_y;

	
	_posGlobal.x_inicio = 0;
	_posGlobal.x_final = 0;
	_posGlobal.y_inicio = 0;
	_posGlobal.y_final = 0;
	_posGlobal.ancho = 0;
	_posGlobal.alto = 0;

	
	// Estos contenedores sólo serán verticales
	// Pase vertical, sumando alturas y calculando el ancho máximo
	for (int i = 0; i < _posActual; i++)
	{
		int *espacio = elems[i]->GetNeededSpace();
		_posGlobal.alto += espacio[1] + _spacing;
		
		_posGlobal.ancho = 
					espacio[0] > _posGlobal.ancho ?
							espacio[0] :
							_posGlobal.ancho;
		
	}

	_posGlobal.alto -= _spacing;

	int centro_efectivo_x = centro_x;
	int centro_efectivo_y = centro_y;

	// Comprobaremos que no se salgan de la pantalla
	if (centro_y - _posGlobal.alto / 2 < 0)
		centro_efectivo_y = _posGlobal.alto / 2 + 5;
	if (centro_y + _posGlobal.alto / 2 > _pantallaY)
		centro_efectivo_y = _pantallaY - _posGlobal.alto / 2 - 25;
	if (centro_x - _posGlobal.ancho / 2 < 0)
		centro_efectivo_x = _posGlobal.ancho / 2 + 5;
	if (centro_x + _posGlobal.ancho / 2 > _pantallaX)
		centro_efectivo_x = _pantallaX - _posGlobal.ancho / 2 - 25;

	int separacion_efectiva_arriba = _spacing;
	int separacion_efectiva_abajo = _spacing;

	if (_bgImage != 0 && _bgImage != -1)
	{
		if (_posImg.alignment == IMG_ALIGN_ABOVE_RIGHT)
		separacion_efectiva_arriba = _spacing * 2;

		else if (_posImg.alignment == IMG_ALIGN_BELOW_RIGHT)
		separacion_efectiva_abajo = _spacing * 2;
	}
	// Ya tenemos lo necesario para decir cuáles serán los bounds del container
	_posGlobal.x_inicio = centro_efectivo_x - _posGlobal.ancho / 2 - _spacing;
	_posGlobal.x_final = centro_efectivo_x + _posGlobal.ancho / 2 + _spacing ;
	_posGlobal.y_inicio = centro_efectivo_y - _posGlobal.alto / 2 - separacion_efectiva_arriba;
	_posGlobal.y_final = centro_efectivo_y + _posGlobal.alto / 2 + separacion_efectiva_abajo;

	// Y para la imagen
	if (_bgImage != 0 && _bgImage != -1)
	{
		switch (_posImg.alignment)
		{
			case IMG_ALIGN_ABOVE_RIGHT:
				_posImg.x_final = _posGlobal.x_final;
				_posImg.y_inicio = _posGlobal.y_inicio;
				_posImg.y_final = _posImg.y_inicio + _posImg.alto;
				_posImg.x_inicio = _posImg.x_final - _posImg.ancho;
				break;
			case IMG_ALIGN_BELOW_RIGHT:
				_posImg.x_final = _posGlobal.x_final;
				_posImg.y_final = _posGlobal.y_final;
				_posImg.y_inicio = _posImg.y_final - _posImg.alto;
				_posImg.x_inicio = _posImg.x_final - _posImg.ancho;
				break;
			case IMG_ALIGN_CENTER:
				_posImg.x_inicio = centro_efectivo_x - _posImg.ancho / 2;
				_posImg.x_final = centro_efectivo_x + _posImg.ancho / 2;
				_posImg.y_inicio = centro_efectivo_y - _posImg.alto / 2;
				_posImg.y_final = centro_efectivo_y + _posImg.alto / 2;
				break;
		}
	}

	int last_height = _posGlobal.y_inicio + separacion_efectiva_arriba;
	
	
	// El centro X de todos los elementos será el mismo, centro_efectivo_x;
	for (int i = 0; i < _posActual; i++)
	{
		int* espacio = elems[i]->GetNeededSpace();
		
		elems[i]->Pack(centro_efectivo_x, last_height + espacio[1] / 2);

		last_height += espacio[1] + _spacing;
		
	}
	
	
	
	
}

void LinkedContainer::Dibujar()
{
	int skip_elems = 0;
	if (_posActual == 0)
		skip_elems = 1;
	if (!EsVisible())
		return;

	if (!_highlighted)
		glEnable(GL_BLEND);

	if (_style & STYLE_BG)
	{// Suavizado o duro
		glColor4f(_bgColor[0], _bgColor[1], _bgColor[2], _transparency);
		if (_style & STYLE_HARD)
			DibujarDuro(GL_FILL);
		else
			DibujarSuavizado (GL_FILL);
//		glColor4f(0.0, 0.0, 0.0, 1.0);
	}
	
	if (!_highlighted)
		glDisable(GL_BLEND);
		
	if (_bgImage != 0 && _bgImage != -1)
	{
		glEnable(GL_BLEND);
		glEnable (GL_TEXTURE_2D);
		glDisable (GL_LIGHTING);
		
		glBindTexture(GL_TEXTURE_2D, _bgImage);
		glBegin(GL_QUADS);
			
			glTexCoord2i(0, 0); glVertex2i(_posImg.x_inicio, _posImg.y_inicio);
			glTexCoord2i(1, 0); glVertex2i(_posImg.x_final, _posImg.y_inicio);
			glTexCoord2i(1, 1); glVertex2i(_posImg.x_final, _posImg.y_final);
			glTexCoord2i(0, 1); glVertex2i(_posImg.x_inicio, _posImg.y_final);
		glEnd();
		
		glDisable(GL_TEXTURE_2D);
		glDisable(GL_BLEND);
	}

	if (_style & STYLE_BORDER)
	{
		glColor4f(0.0, 0.0, 0.0, 1.0);
		if (_style & STYLE_HARD)
			DibujarDuro(GL_LINE);
		else
			DibujarSuavizado (GL_LINE);
	}

		
	if (!skip_elems)
	for (int i = 0; i < _posActual; i++)
		elems[i]->Dibujar();
}

void LinkedContainer::DibujarSuavizado(GLenum mode)
{
	GLUquadricObj *quad;
	quad = gluNewQuadric();
	
	if (mode == GL_FILL)
	{
		glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
		gluQuadricDrawStyle(quad, GL_FILL);

		glBegin(GL_QUADS);
			glVertex2i(_posGlobal.x_inicio, _posGlobal.y_inicio + 20);
			glVertex2i(_posGlobal.x_final, _posGlobal.y_inicio + 20);
			glVertex2i(_posGlobal.x_final, _posGlobal.y_final - 20);
			glVertex2i(_posGlobal.x_inicio, _posGlobal.y_final - 20);
		
			glVertex2i(_posGlobal.x_inicio + 20, _posGlobal.y_inicio);
			glVertex2i(_posGlobal.x_final - 20 , _posGlobal.y_inicio);
			glVertex2i(_posGlobal.x_final - 20, _posGlobal.y_inicio + 20);
			glVertex2i(_posGlobal.x_inicio + 20, _posGlobal.y_inicio + 20);
		
			glVertex2i(_posGlobal.x_inicio + 20, _posGlobal.y_final);
			glVertex2i(_posGlobal.x_final - 20 , _posGlobal.y_final);
			glVertex2i(_posGlobal.x_final - 20, _posGlobal.y_final - 20);
			glVertex2i(_posGlobal.x_inicio + 20, _posGlobal.y_final - 20);
		glEnd();

		glPushMatrix();
		glTranslatef(_posGlobal.x_inicio + 20, _posGlobal.y_inicio + 20, 0);
		gluPartialDisk(quad, 0, 20, 4, 1, 180, 90);
		glPopMatrix();

		glPushMatrix();
		glTranslatef(_posGlobal.x_inicio + 20, _posGlobal.y_final - 20, 0);
		gluPartialDisk(quad, 0, 20, 4, 1, 270, 90);
		glPopMatrix();

		glPushMatrix();
		glTranslatef(_posGlobal.x_final - 20, _posGlobal.y_final - 20, 0);
		gluPartialDisk(quad, 0, 20, 4, 1, 0, 90);
		glPopMatrix();

		glPushMatrix();
		glTranslatef(_posGlobal.x_final - 20, _posGlobal.y_inicio + 20, 0);
		gluPartialDisk(quad, 0, 20, 4, 1, 90, 90);
		glPopMatrix();

	}
	else
	{
		glBegin(GL_LINE_STRIP);
			glVertex2i(_posGlobal.x_inicio, _posGlobal.y_inicio + 20);
			glVertex2i(_posGlobal.x_inicio, _posGlobal.y_final -20);
		glEnd();
		
		glBegin(GL_LINE_STRIP);
			glVertex2i(_posGlobal.x_final, _posGlobal.y_inicio + 20);
			glVertex2i(_posGlobal.x_final, _posGlobal.y_final -20);
		glEnd();
		
		glBegin(GL_LINE_STRIP);
			glVertex2i(_posGlobal.x_inicio + 20, _posGlobal.y_inicio);
			glVertex2i(_posGlobal.x_final - 20, _posGlobal.y_inicio);
		glEnd();
		
		glBegin(GL_LINE_STRIP);
			glVertex2i(_posGlobal.x_inicio + 20, _posGlobal.y_final);
			glVertex2i(_posGlobal.x_final - 20, _posGlobal.y_final);
		glEnd();
		
		gluQuadricDrawStyle(quad, GL_LINE);
		float ancho_linea;
		glGetFloatv(GL_LINE_WIDTH, &ancho_linea);
		
		glPushMatrix();
		glTranslatef(_posGlobal.x_inicio + 20, _posGlobal.y_inicio + 20, 0);
		gluPartialDisk(quad, 20-ancho_linea, 20, 4, 1, 180, 90);
		glPopMatrix();

		glPushMatrix();
		glTranslatef(_posGlobal.x_inicio + 20, _posGlobal.y_final - 20, 0);
		gluPartialDisk(quad, 20-ancho_linea, 20, 4, 1, 270, 90);
		glPopMatrix();

		glPushMatrix();
		glTranslatef(_posGlobal.x_final - 20, _posGlobal.y_final - 20, 0);
		gluPartialDisk(quad, 20-ancho_linea, 20, 4, 1, 0, 90);
		glPopMatrix();

		glPushMatrix();
		glTranslatef(_posGlobal.x_final - 20, _posGlobal.y_inicio + 20, 0);
		gluPartialDisk(quad, 20-ancho_linea, 20, 4, 1, 90, 90);
		glPopMatrix();
	}
	
	free (quad);
	
}

void LinkedContainer::DibujarDuro (GLenum mode)
{
	glPolygonMode(GL_FRONT_AND_BACK, mode);

		glBegin(GL_QUADS);	
			glVertex2i(_posGlobal.x_inicio, _posGlobal.y_inicio);
			glVertex2i(_posGlobal.x_final, _posGlobal.y_inicio);
			glVertex2i(_posGlobal.x_final, _posGlobal.y_final);
			glVertex2i(_posGlobal.x_inicio, _posGlobal.y_final);
		glEnd();
	// Por defecto siempre en GL_FILL
	glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);;
}



SDL_Surface* LinkedContainer::LoadPNG (std::string ruta){
    Uint8 *rowhi, *rowlo;
    Uint8 *tmpbuf, tmpch;
    SDL_Surface *image;
    int i, j;

	SDL_RWops *rwop;
	rwop = SDL_RWFromFile(ruta.c_str(), "rb");
	image = IMG_LoadPNG_RW(rwop);

    if ( image == NULL ) {
		//TODO gestión de erroes
	}

	// Invertimos la imagen.
	// No es necesario invertirla en 2D, nuestro layout ya está "de arriba abajo"
	//  para OpenGL
//	InvertSurface(image);
	
    return(image);
}



	
std::string LinkedContainer::OnDir(int dir)
{
	std::cerr << "Elemento equivocado para OnDir(int dir)" << std::endl;
}
void LinkedContainer::ToDir(int dir, std::string id)
{
	std::cerr << "Elemento equivocado para ToDir(int dir, std::string id)" << std::endl;
}

void LinkedContainer::SetSelectable(int selectable)
{
	std::cerr << "Elemento equivocado para SetSelectable(int selectable)" << std::endl;
}
int LinkedContainer::IsSelectable()
{
	std::cerr << "Elemento equivocado para IsSelectable()" << std::endl;
}
std::string LinkedContainer::GetID()
{
	std::cerr << "Elemento equivocado para GetID()" << std::endl;
}